package com.ihr360.payroll.service.attendancereport.impl;

import com.alibaba.fastjson.JSON;
import com.google.common.collect.Lists;
import com.ihr360.alps.PropertyDescriptor;
import com.ihr360.alps.PropertyType;
import com.ihr360.alps.ServiceDescriptor;
import com.ihr360.alps.api.ServiceProvider;
import com.ihr360.attendance.AttendanceReportFixedPart;
import com.ihr360.authority.sharing.model.vo.SimpleAuthUserResourceVo;
import com.ihr360.authority.sharing.service.AuthUserResourceService;
import com.ihr360.commons.entity.AuthUserInfo;
import com.ihr360.commons.exception.Ihr360Exception;
import com.ihr360.commons.lang.CollectionUtils;
import com.ihr360.commons.lang.DateUtils;
import com.ihr360.commons.model.type.ProgressType;
import com.ihr360.commons.vo.PageData;
import com.ihr360.excel.ExcelUtil;
import com.ihr360.excel.cellstyle.ExcelCellStyle;
import com.ihr360.excel.cellstyle.ExcelCellStyleFactory;
import com.ihr360.excel.entity.CorrectSpreadsheetRowEntity;
import com.ihr360.excel.entity.ExportParams;
import com.ihr360.excel.entity.ImportParams;
import com.ihr360.excel.vo.CustomSpreadSheetValidateResult;
import com.ihr360.excel.vo.ImportRequest;
import com.ihr360.excel.vo.SpreadSheetRow;
import com.ihr360.excel.vo.SpreadSheetValidInterruptError;
import com.ihr360.excel.vo.SpreadSheetValidatorError;
import com.ihr360.excel.vo.SpreadSheetValidatorRequest;
import com.ihr360.payroll.dao.AttendanceBillRepository;
import com.ihr360.payroll.dao.AttendanceMonthlyReportRepository;
import com.ihr360.payroll.dao.AttendanceMonthlyReportSummaryRepository;
import com.ihr360.payroll.importVo.AttendanceSummaryImportVo;
import com.ihr360.payroll.model.dto.attendance.report.AttendanceBill;
import com.ihr360.payroll.model.dto.attendance.report.AttendanceMonthlyReport;
import com.ihr360.payroll.model.dto.attendance.report.AttendanceMonthlyReportSummary;
import com.ihr360.payroll.model.dto.attendance.report.embeddable.AttendanceReportFlexPart;
import com.ihr360.payroll.model.dto.attendance.report.embeddable.AttendanceReportFlexPartHeader;
import com.ihr360.payroll.model.dto.salary.embeddable.StaffInfoSnapshot;
import com.ihr360.payroll.model.dto.salary.plan.SalaryPlan;
import com.ihr360.payroll.model.type.AttendanceSourceType;
import com.ihr360.payroll.request.attendance.AttendanceOverTimeRequest;
import com.ihr360.payroll.request.attendance.AttendanceTakeNumberRequest;
import com.ihr360.payroll.request.attendance.StaffOverTimeRequest;
import com.ihr360.payroll.response.attendance.AttendanceBillSyncResponse;
import com.ihr360.payroll.response.attendance.AttendanceMonthlySyncResponse;
import com.ihr360.payroll.response.attendance.AttendanceSyncResponse;
import com.ihr360.payroll.service.attendancereport.AttendanceMonthlyReportSummaryService;
import com.ihr360.payroll.service.salaryplan.SalaryPlanService;
import com.ihr360.payroll.type.AttendanceState;
import com.ihr360.rest.reposity.SimpleRestReposity;
import com.ihr360.rest.service.AbstractSimpleRestServiceImpl;
import com.ihr360.shared.basicstaffinfo.dao.SyncedStaffInfoRepository;
import com.ihr360.shared.basicstaffinfo.dto.SyncedBasicStaffInfo;
import com.ihr360.sharedservice.api.DataCacheService;
import com.ihr360.validation.ValidationUtils;
import org.apache.commons.collections.MapUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.OutputStream;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;


@Service
public class AttendanceMonthlyReportSummaryServiceImpl extends AbstractSimpleRestServiceImpl<AttendanceMonthlyReportSummary, Long> implements AttendanceMonthlyReportSummaryService {
    @Autowired
    private AttendanceMonthlyReportSummaryRepository attendanceMonthlyReportSummaryRepository;

    @Autowired
    private AttendanceMonthlyReportRepository attendanceMonthlyReportRepository;

    @Autowired
    private AttendanceBillRepository attendanceBillRepository;

    @Autowired
    private SyncedStaffInfoRepository syncedStaffInfoRepository;

    @Autowired
    private SalaryPlanService salaryPlanService;

    @Autowired
    private DataCacheService dataCacheService;

    @Autowired
    private AuthUserResourceService authUserResourceService;

    public static final String SALARY_PLAN_ID = "salaryPlanId";
    public static final String SUMMARY_DATE = "summaryDate";
    public static final String SUMMARY_START_DATE = "summaryStartDate";
    public static final String SUMMARY_END_DATE = "summaryEndDate";
    public static final String SALARY_START_DATE = "salaryStartDate";
    public static final String SALARY_END_DATE = "salaryEndDate";

    @Override
    public SimpleRestReposity<AttendanceMonthlyReportSummary, Long> getSimpleRestReposity() {
        return attendanceMonthlyReportSummaryRepository;
    }

    private static final Logger logger = LoggerFactory.getLogger(AttendanceMonthlyReportSummaryServiceImpl.class);

    @Override
    public PageData<AttendanceMonthlyReportSummary> getSummaryList(AuthUserInfo userInfo, Pageable pageable) {
        List<AttendanceMonthlyReportSummary> list = Lists.newArrayList();
        List<SimpleAuthUserResourceVo> authList = authUserResourceService.queryUserResources(userInfo.getCompanyId(), AttendanceMonthlyReportSummary.class, userInfo.getUserId());
        if (CollectionUtils.isEmpty(authList)) {
            return new PageData<>(0, 0, list);
        }
        List<Long> summaryIdList = Lists.newArrayList();
        authList.forEach(authResource -> {
            summaryIdList.add(Long.parseLong(authResource.getResourceId()));
        });
        Page<AttendanceMonthlyReportSummary> page = attendanceMonthlyReportSummaryRepository.findByCompanyIdAndIdInOrderByCreatedAtDesc(userInfo.getCompanyId(), summaryIdList, pageable);
        if (CollectionUtils.isNotEmpty(page.getContent())) {
            list = page.getContent();
        }
        return new PageData<>(page.getTotalPages(), page.getTotalElements(), list);
    }

    @Override
    public void excelTemplateDownload(OutputStream out) {
        ExportParams<List<Object>> exportParams = new ExportParams<>();
        Map<String, String> headerMap = new LinkedHashMap<>();
        Map<String, ExcelCellStyle> headerStyleMap = new HashMap<>();

        headerMap.put("staffName", "姓名");
        headerMap.put("mobile", "手机号");
        //headerMap.put("attendanceCycle", "考勤周期");
        headerMap.put("numberOfDay", AttendanceReportFixedPart.FieldName.PAID_DAYS);
        //headerMap.put("workingDay", AttendanceReportFixedPart.FieldName.MONTH_PAID_DAYS);
        headerMap.put("attendanceDay", AttendanceReportFixedPart.FieldName.SUPPOSED_WORKING_DAYS);
        headerMap.put("actualAttendanceDay", AttendanceReportFixedPart.FieldName.WORKING_DAYS);
        headerMap.put("absenceFromDutyDay", AttendanceReportFixedPart.FieldName.ABSENCE_FROM_DUTY_DAYS);
        headerMap.put("nonWorkingDays", AttendanceReportFixedPart.FieldName.NON_WORKING_DAYS);


        headerMap.put("overtimeWeekday", AttendanceReportFixedPart.FieldName.OT_HOURS_150);
        headerMap.put("restDayWeekday", AttendanceReportFixedPart.FieldName.OT_HOURS_200);
        headerMap.put("holidayOvertime", AttendanceReportFixedPart.FieldName.OT_HOURS_300);

        headerMap.put("workingDayOvertimeCal", AttendanceReportFixedPart.FieldName.WORKINGDAY_OVERTIME_CAL);
        headerMap.put("restDayOvertimeCal", AttendanceReportFixedPart.FieldName.RESTDAY_OVERTIME_CAL);
        headerMap.put("holidayOvertimeCal", AttendanceReportFixedPart.FieldName.HOLIDAY_OVERTIME_CAL);

        headerMap.put("deductionSickLeave", AttendanceReportFixedPart.FieldName.SICK_LEAVE_COUNT);
        //headerMap.put("fullPaySickLeave", "全薪病假时长");
        headerMap.put("leaveTime", AttendanceReportFixedPart.FieldName.UNPAID_LEAVE_COUNT);
        headerMap.put("maternityDays", AttendanceReportFixedPart.FieldName.MATERNITY_COUNT);
        headerMap.put("annualTime", AttendanceReportFixedPart.FieldName.ANNUAL_LEAVETIME);
        headerMap.put("restHours", AttendanceReportFixedPart.FieldName.REST_HOURS);
        //headerMap.put("otherDeductions", "其它假时长");

        headerMap.put("syntheticalAdjust", AttendanceReportFixedPart.FieldName.SYNTHETICAL_ADJUST);
        headerMap.put("syntheticalOthours300", AttendanceReportFixedPart.FieldName.SYNTHETICAL_OTHOURS_300);
        headerMap.put("syntheticalWorkHourMonth", AttendanceReportFixedPart.FieldName.SYNTHETICAL_WORKHOURMONTH);
        headerMap.put("synthetialWorkHourQuarter", AttendanceReportFixedPart.FieldName.SYNTHETICAL_WORKHOURQUARTER);
        headerMap.put("syntheticalWorkHourYear", AttendanceReportFixedPart.FieldName.SYNTHETICAL_WORKHOURYEAR);


        headerMap.put("lateArrival", AttendanceReportFixedPart.FieldName.LATE_ARRIVALS);
        headerMap.put("returnEarly", AttendanceReportFixedPart.FieldName.RETURN_EARLY);

        headerMap.put("signInMiss", AttendanceReportFixedPart.FieldName.SIGN_IN_MISS);
        headerMap.put("signOutMiss", AttendanceReportFixedPart.FieldName.SIGN_OUT_MISS);
        headerMap.put("lateMinutes", AttendanceReportFixedPart.FieldName.LATE_MINUTES);
        headerMap.put("earlyMinutes", AttendanceReportFixedPart.FieldName.EARLY_MINUTES);
        //headerMap.put("absenceFromDuty", AttendanceReportFixedPart.FieldName.ABSENCE_FROM_DUTY);


        headerStyleMap.put("staffName", ExcelCellStyleFactory.createRequiredHeaderCellStyle());
        headerStyleMap.put("mobile", ExcelCellStyleFactory.createRequiredHeaderCellStyle());
        //headerStyleMap.put("attendanceCycle", ExcelCellStyleFactory.createRequiredHeaderCellStyle());
        headerStyleMap.put("numberOfDay", ExcelCellStyleFactory.createRequiredHeaderCellStyle());

        exportParams.setHeaderMap(headerMap);
        exportParams.setHeaderStyleMap(headerStyleMap);

        ExcelUtil.exportExcel(exportParams, out);
    }


    @Override
    @Transactional
    public void importSummary(AuthUserInfo userInfo, ImportRequest importRequest) {
        ImportParams<AttendanceSummaryImportVo> importParams = new ImportParams<>();
        importParams.setImportType(AttendanceSummaryImportVo.class);
        try {
            List<CorrectSpreadsheetRowEntity> excelData = importRequest.getCorrectDatas();
            Map<String, Object> extraParams = importRequest.getExtraParams();
            Long salaryPlanId = Long.parseLong(extraParams.get(SALARY_PLAN_ID).toString());
            Date summaryDate = DateUtils.parse(extraParams.get(SUMMARY_DATE).toString());
            Date summaryStartDate = DateUtils.parse(extraParams.get(SUMMARY_START_DATE).toString());
            Date summaryEndDate = DateUtils.parse(extraParams.get(SUMMARY_END_DATE).toString());
            Date salaryStartDate = DateUtils.parse(extraParams.get(SALARY_START_DATE).toString());
            Date salaryEndDate = DateUtils.parse(extraParams.get(SALARY_END_DATE).toString());

            SalaryPlan salaryPlan = salaryPlanService.getOne(salaryPlanId);
            if (salaryPlan == null) {
                throw new Ihr360Exception(null, "未查询到该薪资方案");
            }
            List<String> staffIdList = excelData.stream().map(CorrectSpreadsheetRowEntity::getStaffId).collect(Collectors.toList());
            List<SyncedBasicStaffInfo> staffInfos = syncedStaffInfoRepository.findByCompanyIdAndIdIn(userInfo.getCompanyId(), staffIdList);
            Map<String, SyncedBasicStaffInfo> staffInfoMap = staffInfos.stream().collect(Collectors.toMap(SyncedBasicStaffInfo::getId, Function.identity()));

            String remark = salaryPlan.getName() + DateUtils.formatDateStr(summaryDate, "yyyy年MM月");
            AttendanceMonthlyReportSummary summary = this.saveSummary(userInfo, summaryStartDate, summaryEndDate, summaryDate, remark, AttendanceSourceType.IMPORT, salaryPlan);
            summary.setSalaryStartDate(salaryStartDate);
            summary.setSalaryEndDate(salaryEndDate);
            summary.setSalaryPlanId(salaryPlan.getId());
            summary.setSalaryPlanName(salaryPlan.getName());
            summary = attendanceMonthlyReportSummaryRepository.save(summary);
            List<AttendanceMonthlyReport> reportList = Lists.newArrayList();
            for (CorrectSpreadsheetRowEntity summaryImportVo : excelData) {
                //获取员工
                SyncedBasicStaffInfo staff = staffInfoMap.get(summaryImportVo.getStaffId());
                if (staff == null) {
                    continue;
                }
                AttendanceMonthlyReport attendanceMonthlyReport = this.importVoToReport(summaryImportVo, staff, summary);
                if (attendanceMonthlyReport == null) {
                    continue;
                }
                reportList.add(attendanceMonthlyReport);
            }
            attendanceMonthlyReportRepository.save(reportList);
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            throw new Ihr360Exception(null, "保存数据失败");
        }
    }

    private AttendanceMonthlyReport importVoToReport(CorrectSpreadsheetRowEntity summaryImportVo, SyncedBasicStaffInfo staffInfo, AttendanceMonthlyReportSummary summary) {
        AttendanceMonthlyReport report = new AttendanceMonthlyReport();
        StaffInfoSnapshot snapshot = new StaffInfoSnapshot();
        snapshot.setStaffId(staffInfo.getId());
        snapshot.setIdCardType(staffInfo.getIdCardType());
        snapshot.setMobileNo(staffInfo.getMobileNo());
        snapshot.setIdCardNo(staffInfo.getIdCardNo());
        snapshot.setStaffName(staffInfo.getStaffName());
        report.setStaffInfo(snapshot);
        report.setYear(summary.getYear());
        report.setMonth(summary.getMonth());
        report.setStartDate(summary.getStartDate());
        report.setEndDate(summary.getEndDate());
        report.setSalaryPlanId(summary.getSalaryPlanId());
        com.ihr360.payroll.model.dto.attendance.report.embeddable.AttendanceReportFixedPart fixedPart = new com.ihr360.payroll.model.dto.attendance.report.embeddable.AttendanceReportFixedPart();
        //BeanUtils.copyProperties(summaryImportVo, fixedPart);
        Map<String, Object> data = summaryImportVo.getData();
        if (CollectionUtils.isNotEmpty(data)) {
            fixedPart.setWorkingdays(getDataValue(data, AttendanceReportFixedPart.FieldName.WORKING_DAYS));
            fixedPart.setSupposedworkingdays(getDataValue(data, AttendanceReportFixedPart.FieldName.SUPPOSED_WORKING_DAYS));

            /**
             * 月计薪天数（标准工作天数，21.75）
             */
            fixedPart.setPaiddays(getDataValue(data, AttendanceReportFixedPart.FieldName.PAID_DAYS));
//            /**
//             * 月计薪天数（包含节假日，23）
//             */
//            fixedPart.setMonthpaiddays(getDataValue(data, AttendanceReportFixedPart.FieldName.MONTH_PAID_DAYS));

            fixedPart.setAbsencefromdutydays(getDataValue(data, AttendanceReportFixedPart.FieldName.ABSENCE_FROM_DUTY_DAYS));
            fixedPart.setNonWorkingDays(getDataValue(data, AttendanceReportFixedPart.FieldName.NON_WORKING_DAYS));

            /**
             * 平日加班小时-标准
             */
            fixedPart.setOthours150(getDataValue(data, AttendanceReportFixedPart.FieldName.OT_HOURS_150));
            /**
             * 周末加班小时-标准
             */
            fixedPart.setOthours200(getDataValue(data, AttendanceReportFixedPart.FieldName.OT_HOURS_200));
            /**
             * 节假日加班小时-标准
             */
            fixedPart.setOthours300(getDataValue(data, AttendanceReportFixedPart.FieldName.OT_HOURS_300));

            /**
             * 工作日加班累计（小时）
             */
            fixedPart.setWorkingdayovertimecal(getDataValue(data, AttendanceReportFixedPart.FieldName.WORKINGDAY_OVERTIME_CAL));
            /**
             * 休息日加班累计（小时）
             */
            fixedPart.setRestdayovertimecal(getDataValue(data, AttendanceReportFixedPart.FieldName.RESTDAY_OVERTIME_CAL));
            /**
             * 节假日加班累计（小时）
             */
            fixedPart.setHolidayovertimecal(getDataValue(data, AttendanceReportFixedPart.FieldName.HOLIDAY_OVERTIME_CAL));

            /**
             * 病假时长
             */
            fixedPart.setSickleavecnt(getDataValue(data, AttendanceReportFixedPart.FieldName.SICK_LEAVE_COUNT));

            /**
             * 事假时长
             */
            fixedPart.setUnpaidleavecnt(getDataValue(data, AttendanceReportFixedPart.FieldName.UNPAID_LEAVE_COUNT));
            /**
             * 产假时长
             */
            fixedPart.setMaternitycnt(getDataValue(data, AttendanceReportFixedPart.FieldName.MATERNITY_COUNT));
            /**
             * 工时调整-综合
             */
            fixedPart.setSyntheticaladjust(getDataValue(data, AttendanceReportFixedPart.FieldName.SYNTHETICAL_ADJUST));

            /**
             * 节假日加班小时-综合
             */
            fixedPart.setSyntheticalothours300(getDataValue(data, AttendanceReportFixedPart.FieldName.SYNTHETICAL_OTHOURS_300));
            /**
             * 月工作日工时-综合
             */
            fixedPart.setSyntheticalworkhourmonth(getDataValue(data, AttendanceReportFixedPart.FieldName.SYNTHETICAL_WORKHOURMONTH));
            /**
             * 季工作日工时-综合
             */
            fixedPart.setSyntheticalworkhourquarter(getDataValue(data, AttendanceReportFixedPart.FieldName.SYNTHETICAL_WORKHOURQUARTER));
            /**
             * 年工作日工时-综合
             */
            fixedPart.setSyntheticalworkhouryear(getDataValue(data, AttendanceReportFixedPart.FieldName.SYNTHETICAL_WORKHOURYEAR));
            /**
             * 年假时长
             */
            fixedPart.setAnnualleavetime(getDataValue(data, AttendanceReportFixedPart.FieldName.ANNUAL_LEAVETIME));
            /**
             * 调休时长
             */
            fixedPart.setResthours(getDataValue(data, AttendanceReportFixedPart.FieldName.REST_HOURS));
            /**
             * 迟到次数（原始次数）
             */
            fixedPart.setLatearrivals(getDataValue(data, AttendanceReportFixedPart.FieldName.LATE_ARRIVALS));
            /**
             * 早退次数（原始次数）
             */
            fixedPart.setReturnearly(getDataValue(data, AttendanceReportFixedPart.FieldName.RETURN_EARLY));
            /**
             *  签到遗漏次数（原始次数）
             * */
            fixedPart.setSigninmiss(getDataValue(data, AttendanceReportFixedPart.FieldName.SIGN_IN_MISS));
            /**
             *  签退遗漏次数（原始次数）
             * */
            fixedPart.setSignoutmiss(getDataValue(data, AttendanceReportFixedPart.FieldName.SIGN_OUT_MISS));
            /**
             *  累计迟到分钟数
             * */
            fixedPart.setLateminutes(getDataValue(data, AttendanceReportFixedPart.FieldName.LATE_MINUTES));
            /**
             *  累计早退分钟数
             * */
            fixedPart.setEarlyminutes(getDataValue(data, AttendanceReportFixedPart.FieldName.EARLY_MINUTES));
            /**
             *  缺勤次数
             * */
            //fixedPart.setAbsencefromduty(getDataValue(data, AttendanceReportFixedPart.FieldName.ABSENCE_FROM_DUTY));
        }
        report.setFixedPart(fixedPart);
        report.setSummary(summary);
        return report;
    }

    private double getDataValue(Map<String, Object> map, String key) {
        double value = 0;
        Object object = map.get(key);
        if (object == null) {
            return value;
        } else {
            try {
                value = Double.parseDouble(object.toString());
            } catch (Exception e) {
                return value;
            }
            return value;
        }
    }

    private void setSummaryYearMonth(AttendanceMonthlyReportSummary summary, Date summaryDate, SalaryPlan salaryPlan) {
        Integer salaryMonth = salaryPlan.getSalaryMonth();

        ValidationUtils.rejectIfEmpty(salaryMonth, null, "薪资方案未设置薪资所属月，请设置");
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(summaryDate);
        calendar.add(Calendar.MONTH, salaryMonth);

        summary.setYear(calendar.get(Calendar.YEAR));
        summary.setMonth(calendar.get(Calendar.MONTH) + 1);
    }


    private AttendanceMonthlyReportSummary saveSummary(AuthUserInfo userInfo, Date summaryStartDate, Date summaryEndDate, Date summaryDate, String remark, AttendanceSourceType sourceType, SalaryPlan salaryPlan) {
        AttendanceMonthlyReportSummary summary = new AttendanceMonthlyReportSummary();
        StaffInfoSnapshot snapshot = new StaffInfoSnapshot();
        snapshot.setStaffId(userInfo.getStaffId());
        snapshot.setIdCardType("");
        snapshot.setMobileNo(userInfo.getMobileNo());
        snapshot.setIdCardNo("");
        snapshot.setStaffName("");
        summary.setStaffInfo(snapshot);

        setSummaryYearMonth(summary, summaryDate, salaryPlan);
        summary.setStartDate(summaryStartDate);
        summary.setEndDate(summaryEndDate);
        summary.setSourceType(sourceType);
        summary.setRemark(remark);
        summary.setCompanyId(userInfo.getCompanyId());
        return summary;
    }

    public Boolean checkSummaryExist(String companyId, int year, int month, Long salaryPlanId) {
        Boolean isExist = false;
        AttendanceMonthlyReportSummary summary = attendanceMonthlyReportSummaryRepository.findByCompanyIdAndYearAndMonthAndSalaryPlanId(companyId, year, month, salaryPlanId);
        if (summary != null) {
            isExist = true;
        }
        return isExist;
    }


    @Override
    @Transactional
    public Long takeNumberSummary(AuthUserInfo userInfo, AttendanceTakeNumberRequest takeNumberRequest) {
        SalaryPlan salaryPlan = salaryPlanService.getOne(takeNumberRequest.getSalaryPlanId());
        if (salaryPlan == null) {
            throw new Ihr360Exception(null, "未查询到该薪资方案");
        }
        String remark = salaryPlan.getName() + DateUtils.formatDateStr(takeNumberRequest.getSummaryDate(), "yyyy年MM月");
        AttendanceMonthlyReportSummary summary = this.saveSummary(userInfo, takeNumberRequest.getSummaryStartDate(), takeNumberRequest.getSummaryEndDate(), takeNumberRequest.getSummaryDate(), remark, AttendanceSourceType.TAKE_NUMBER, salaryPlan);
        if (this.checkSummaryExist(userInfo.getCompanyId(), summary.getYear(), summary.getMonth(), summary.getSalaryPlanId())) {
            throw new Ihr360Exception(null, "同一薪资方案同一日期只可存在一条汇总记录");
        }
        summary.setSalaryStartDate(takeNumberRequest.getSalaryStartDate());
        summary.setSalaryEndDate(takeNumberRequest.getSalaryEndDate());
        summary.setOverTimeStartDate(takeNumberRequest.getOverTimeStartDate());
        summary.setOverTimeEndDate(takeNumberRequest.getOverTimeEndDate());
        summary.setSalaryPlanName(salaryPlan.getName());
        summary.setSalaryPlanId(salaryPlan.getId());
        summary = attendanceMonthlyReportSummaryRepository.save(summary);
        return summary.getId();
    }

    @Override
    public void takeNumberMonthlyReport(AuthUserInfo userInfo, AttendanceTakeNumberRequest takeNumberRequest) {
        List<SyncedBasicStaffInfo> staffInfos = syncedStaffInfoRepository.findByCompanyIdAndIdIn(userInfo.getCompanyId(), takeNumberRequest.getStaffIds());
        Map<String, SyncedBasicStaffInfo> staffInfoMap = staffInfos.stream().collect(Collectors.toMap(SyncedBasicStaffInfo::getId, Function.identity()));
        if (takeNumberRequest.getSummaryId() == null) {
            throw new Ihr360Exception(null, "考勤汇总记录不存在");
        }
        AttendanceMonthlyReportSummary summary = this.getOne(takeNumberRequest.getSummaryId());
        if (summary == null) {
            throw new Ihr360Exception(null, "考勤汇总记录不存在");
        }
        //获取月报
        AttendanceSyncResponse monthResponse = takeNumberRequest.getMonthResponse();
        if (monthResponse != null) {
            AttendanceReportFlexPartHeader flexPartHeader = new AttendanceReportFlexPartHeader();
            flexPartHeader.setHeader(monthResponse.getHeader());
            summary.setFlexPartHeader(flexPartHeader);
        }
        //获取日报数据
        List<AttendanceBillSyncResponse> billList = takeNumberRequest.getBillList();
        //月报列表数据处理
        if (monthResponse == null) {
            return;
        }
        List<AttendanceMonthlyReport> monthlyReportList = this.getMonthlyReportList(monthResponse.getMonthlyResponseList(), summary, staffInfoMap);
        try {
            attendanceMonthlyReportRepository.save(monthlyReportList);
            if (CollectionUtils.isEmpty(billList)) {
                return;
            }
            List<AttendanceBill> bills = this.getAttendanceBillList(monthlyReportList, billList, staffInfoMap);
            attendanceBillRepository.save(bills);
        } catch (Exception e) {
            logger.error("考勤取数生成失败，请求参数:" + JSON.toJSONString(takeNumberRequest));
            logger.error("考勤取数同步异常", e);
            //删除失败的汇总记录
            attendanceMonthlyReportSummaryRepository.delete(takeNumberRequest.getSummaryId());
            dataCacheService.remove(ProgressType.ATTENDANCE_SUMMARY.name() + takeNumberRequest.getSummaryId());
        }
    }

    private List<AttendanceBill> getAttendanceBillList(List<AttendanceMonthlyReport> monthlyReports, List<AttendanceBillSyncResponse> billSyncResponses, Map<String, SyncedBasicStaffInfo> staffInfoMap) {
        List<AttendanceBill> list = Lists.newArrayList();
        if (CollectionUtils.isEmpty(monthlyReports) || CollectionUtils.isEmpty(billSyncResponses)) {
            return list;
        }
        Map<String, AttendanceMonthlyReport> monthlyReportMap = monthlyReports.stream().collect(Collectors.toMap(monthlyReport -> monthlyReport.getStaffInfo().getStaffId(), Function.identity()));
        for (AttendanceBillSyncResponse response : billSyncResponses) {
            if (!staffInfoMap.containsKey(response.getStaffId())) {
                //不存在用户
                continue;
            }
            if (!monthlyReportMap.containsKey(response.getStaffId())) {
                //不存在月报数据
                continue;
            }
            SyncedBasicStaffInfo staffInfo = staffInfoMap.get(response.getStaffId());
            AttendanceMonthlyReport monthlyReport = monthlyReportMap.get(response.getStaffId());
            AttendanceBill bill = new AttendanceBill();
            StaffInfoSnapshot snapshot = new StaffInfoSnapshot();
            BeanUtils.copyProperties(staffInfo, snapshot);
            snapshot.setStaffId(staffInfo.getId());
            bill.setStaffInfo(snapshot);
            bill.setCompanyId(monthlyReport.getCompanyId());
            bill.setMonthlyReport(monthlyReport);
            bill.setMonth(monthlyReport.getMonth());
            bill.setYear(monthlyReport.getYear());
            bill.setItem(response.getItem());
            bill.setReportType(response.getReportType());
            bill.setApplyDate(response.getApplyDate());
            bill.setRemark(response.getRemark());
            bill.setReportDate(response.getReportDate());
            bill.setSalaryPlanId(monthlyReport.getSalaryPlanId());
            list.add(bill);
        }
        return list;
    }

    private List<AttendanceMonthlyReport> getMonthlyReportList(List<AttendanceMonthlySyncResponse> monthlySyncResponses, AttendanceMonthlyReportSummary summary, Map<String, SyncedBasicStaffInfo> staffInfoMap) {

        List<AttendanceMonthlyReport> list = Lists.newArrayList();
        if (CollectionUtils.isEmpty(monthlySyncResponses)) {
            return list;
        }
        for (AttendanceMonthlySyncResponse monthlySyncResponse : monthlySyncResponses) {
            if (!staffInfoMap.containsKey(monthlySyncResponse.getStaffId())) {
                continue;
            }
            SyncedBasicStaffInfo staffInfo = staffInfoMap.get(monthlySyncResponse.getStaffId());
            AttendanceMonthlyReport report = new AttendanceMonthlyReport();
            StaffInfoSnapshot snapshot = new StaffInfoSnapshot();
            BeanUtils.copyProperties(staffInfo, snapshot);
            snapshot.setStaffId(staffInfo.getId());
            report.setStaffInfo(snapshot);
            report.setCompanyId(summary.getCompanyId());
            report.setSummary(summary);
            //薪资方案id
            report.setSalaryPlanId(summary.getSalaryPlanId());
            report.setStartDate(summary.getStartDate());
            report.setEndDate(summary.getEndDate());
            report.setMonth(summary.getMonth());
            report.setYear(summary.getYear());
            com.ihr360.payroll.model.dto.attendance.report.embeddable.AttendanceReportFixedPart fixedPart = new com.ihr360.payroll.model.dto.attendance.report.embeddable.AttendanceReportFixedPart();
            BeanUtils.copyProperties(monthlySyncResponse.getFixedPart(), fixedPart);
            report.setFixedPart(fixedPart);
            AttendanceReportFlexPart flexPart = new AttendanceReportFlexPart();
            flexPart.setItems(monthlySyncResponse.getItems());
            report.setFlexPart(flexPart);
            list.add(report);
        }
        return list;
    }

    @Override
    public ServiceDescriptor serviceDescriptor(String companyId) {
        List<AttendanceMonthlyReportSummary> summaryList = attendanceMonthlyReportSummaryRepository.findByCompanyIdOrderByStartDateAsc(companyId);
        if (CollectionUtils.isEmpty(summaryList)) {
            return null;
        }
        Map<String, String> head = new HashMap<>();
        for (AttendanceMonthlyReportSummary summary : summaryList) {
            if (summary.getFlexPartHeader() == null || MapUtils.isEmpty(summary.getFlexPartHeader().getHeader())) {
                continue;
            }
            head.putAll(summary.getFlexPartHeader().getHeader());
        }
        List<PropertyDescriptor> propertyDescriptors = Lists.newArrayListWithCapacity(head.size());
        head.forEach((code, name) -> {
            propertyDescriptors.add(PropertyDescriptor.builder()
                    .id(code)
                    .name(name)
                    .type(PropertyType.DOUBLE)
                    .build());
        });
        return ServiceDescriptor.builder()
                .serviceId(ServiceProvider.SERVICE_ID.ATTENDANCE_MONTHLY_REPORT)
                .name("考勤")
                .path("/alps/" + ServiceProvider.SERVICE_ID.ATTENDANCE_MONTHLY_REPORT)
                .descriptors(propertyDescriptors)
                .build();
    }

    @Override
    @Transactional
    public AttendanceOverTimeRequest lockAttendanceState(Long summaryId, AttendanceState state) {
        AttendanceOverTimeRequest request = new AttendanceOverTimeRequest();
        AttendanceMonthlyReportSummary summary = this.getOne(summaryId);
        if (summary == null) {
            throw new Ihr360Exception(null, "考勤汇总记录不存在");
        }
        request.setSummaryId(summaryId);
        Boolean isImport = AttendanceSourceType.IMPORT.equals(summary.getSourceType());
        request.setIsImport(isImport);
        request.setState(summary.getState());//将汇总信息原始状态返回，方便回滚汇总状态
        summary.setState(state);
        this.createOrUpdate(summary.getId(), summary);
        List<AttendanceMonthlyReport> monthlyReportList = summary.getAttendanceMonthlyReportList();
        if (CollectionUtils.isEmpty(monthlyReportList)) {
            return request;
        }
        List<StaffOverTimeRequest> list = Lists.newArrayList();
        monthlyReportList.forEach(attendanceMonthlyReport -> {
            StaffOverTimeRequest overTimeRequest = new StaffOverTimeRequest();
            overTimeRequest.setStaffId(attendanceMonthlyReport.getStaffInfo().getStaffId());
            overTimeRequest.setWorkingDayOvertime(attendanceMonthlyReport.getFixedPart().getOthours150());//工作日加班
            overTimeRequest.setRestDayOvertime(attendanceMonthlyReport.getFixedPart().getOthours200());//休息日加班
            overTimeRequest.setHolidayOvertime(attendanceMonthlyReport.getFixedPart().getOthours300());//节假日加班
            list.add(overTimeRequest);
        });
        request.setStaffOverTimeRequestList(list);
        return request;
    }

    @Override
    public CustomSpreadSheetValidateResult validateExcelData(String companyId, String userId, Long salaryPlanId, Date summaryDate, Date summaryStartDate, Date summaryEndDate, Date salaryStartDate, Date salaryEndDate, SpreadSheetValidatorRequest validatorRequest) {
        ValidationUtils.rejectIfEmpty(salaryPlanId, "", "薪资方案不可为空");
        ValidationUtils.rejectIfEmpty(summaryDate, "", "考勤日期不能为空");
        ValidationUtils.rejectIfEmpty(summaryStartDate, "", "考勤开始日期不能为空");
        ValidationUtils.rejectIfEmpty(summaryEndDate, "", "考勤结束日期不能为空");
        ValidationUtils.rejectIfEmpty(salaryStartDate, "", "薪资开始日期不能为空");
        ValidationUtils.rejectIfEmpty(salaryEndDate, "", "薪资结束日期不能为空");


        CustomSpreadSheetValidateResult result = new CustomSpreadSheetValidateResult();
        List<SpreadSheetRow> excelDatas = validatorRequest.getSpreadSheetDatas();

        Calendar c = Calendar.getInstance();
        c.setTime(summaryDate);

        //校验该薪资方案是否导入同步过该月数据
        Boolean isExist = checkSummaryExist(companyId, c.get(Calendar.YEAR), c.get(Calendar.MONTH) + 1, salaryPlanId);
        if (isExist) {
            SpreadSheetValidInterruptError interruptError = new SpreadSheetValidInterruptError();
            interruptError.setDefaultLogMsg("该薪资方案该月已存在汇总数据");
            result.setInterruptError(interruptError);
            return result;
        }
        if (CollectionUtils.isEmpty(excelDatas)) {
            return result;
        }
        List<String> existStaff = Lists.newArrayList();
        List<SpreadSheetValidatorError> errors = Lists.newArrayList();
        for (SpreadSheetRow sheetRow : excelDatas) {
            if (existStaff.contains(sheetRow.getStaffId())) {
                SpreadSheetValidatorError error = new SpreadSheetValidatorError();
                error.setMsg("员工在表格中已存在");
                error.setRowNum(sheetRow.getRowNum());
                errors.add(error);
                continue;
            }
            existStaff.add(sheetRow.getStaffId());
        }
        result.setErrors(errors);
        return result;
    }
}